﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace mhxp
{
	public partial class SaveHxpForm : Form
	{
		private Thread _thd;
		private bool _work;

		private List<Bitmap> _bmps;
		private BinaryWriter _file = null;

		private int _fullsize;
		private List<int> _sizes = new List<int>();

		private delegate void DgtDoClose();
		private delegate void DgtUpdateProgress(int n);

		public SaveHxpForm(string filename, List<Bitmap> bmps, int framepersecond, Point pixsize)
		{
			InitializeComponent();

			_bmps = bmps;

			//
			OpenFile(filename, framepersecond, pixsize);

			//
			prgWork.Maximum = bmps.Count;
		}

		private void SaveHxp_Load(object sender, EventArgs e)
		{
			_work = true;
			_thd = new Thread(new ThreadStart(ThreadProc));
			_thd.Start();
		}

		private void SaveHxp_FormClosing(object sender, FormClosingEventArgs e)
		{
			CloseFile();
		}

		private void btnCancel_Click(object sender, EventArgs e)
		{
			_work = false;
			_thd.Join();
			this.Close();
		}

		private void UpdateProgress(int n)
		{
			prgWork.Value = n >= prgWork.Maximum ? prgWork.Maximum : n;
		}

		private void ThreadProc()
		{
			int index = 0;

			while (_work)
			{
				this.Invoke(new DgtUpdateProgress(UpdateProgress), new object[] { index + 1 });

				int orgsize, size = SaveImage(_bmps[index], out orgsize);
				_fullsize += size;
				_sizes.Add(size);

				System.Diagnostics.Debug.WriteLine("IMAGE#{0} {1} -> {2}", index, orgsize, size);

				//
				index++;
				if (index == _bmps.Count)
					break;

				Thread.Sleep(10);
			}

			if (index == _bmps.Count)
				this.Invoke(new DgtDoClose(Close));
		}

		private void OpenFile(string filename, int framepersecond, Point pixsize)
		{
			FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
			_file = new BinaryWriter(fs);

			// hs 헤더
			HsHeader hsheader = new HsHeader();
			hsheader.hdr = SBCS.ToFourCC("SBQG");
			hsheader.version = 10;
			hsheader.revision = 0;
			hsheader.timestamp = 0;
			_file.Write(SBCS.StructToBytes(hsheader));

			// hs 아이템 -> hxp
			HsItem hsitem = new HsItem();
			hsitem.hdr = SBCS.ToFourCC("HXP ");
			hsitem.type = 0x1005;
			hsitem.version = 1;
			hsitem.length = 0;	// 나중에 이것만 고쳐야함
			hsitem.reserved = 0;
			_file.Write(SBCS.StructToBytes(hsitem));

			// hxp 헤더
			HxpHeader hxpheader = new HxpHeader();
			hxpheader.count = (uint)_bmps.Count;
			hxpheader.insts = 0;
			hxpheader.framepersecond = (ushort)framepersecond;
			hxpheader.width = (ushort)pixsize.X;
			hxpheader.height = (ushort)pixsize.Y;
			hxpheader.bpp = 4;
			_file.Write(SBCS.StructToBytes(hxpheader));

			// 아이템 크기 넣기
			for (int i = 0; i < _bmps.Count; i++)
				_file.Write((int)0);

			//
			System.Diagnostics.Debug.WriteLine("이미지 크기: {0}x{1} = {2}",
				_bmps[0].Width, _bmps[0].Height, _bmps[0].Width * _bmps[0].Height * 4);
		}

		private void CloseFile()
		{
			if (_file == null)
				return;

			HsTail hstail = new HsTail();
			hstail.hdr = SBCS.ToFourCC("ENDQ");
			hstail.version = 10;
			hstail.revision = 0;
			_file.Write(SBCS.StructToBytes(hstail));

			int pos, size;

			// 토탈 사이즈 위치
			pos =
				Marshal.SizeOf(typeof(HsHeader)) +
				Marshal.SizeOf(typeof(HsItem)) - Marshal.SizeOf(typeof(uint)) * 2;
			size =
				_fullsize +
				_bmps.Count * Marshal.SizeOf(typeof(int)) +
				Marshal.SizeOf(typeof(HxpHeader));
			_file.Seek(pos, SeekOrigin.Begin);
			_file.Write(size);

			// 개별 사이즈 위치
			pos =
				Marshal.SizeOf(typeof(HsHeader)) +
				Marshal.SizeOf(typeof(HsItem)) +
				Marshal.SizeOf(typeof(HxpHeader));
			_file.Seek(pos, SeekOrigin.Begin);

			foreach (int s in _sizes)
				_file.Write(s);

			//
			_file.Close();
		}

		private int SaveImage(Bitmap bmp, out int orgsize)
		{
			BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

			orgsize = bmpdata.Stride * bmpdata.Height;
			byte[] buf = new byte[orgsize];

			Marshal.Copy(bmpdata.Scan0, buf, 0, orgsize);

			bmp.UnlockBits(bmpdata);

			//
			byte[] zbf = SBCS.ZCompress(buf);

			if (zbf == null)
				return 0;

			_file.Write(zbf);

			return zbf.Length;
		}
	}
}
